#include <i264e_common.h>
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
int i264e_icodec_io_init(i264e_t *h, i264e_icodec_io_t *icodec_io)
{
    return i264e_ratecontrol_priv_init(h, &icodec_io->protocol.h264e.priv, &icodec_io->protocol.h264e.priv_size);
}

void i264e_icodec_io_deinit(i264e_t *h, i264e_icodec_io_t *icodec_io)
{
    i264e_ratecontrol_priv_deinit(h, icodec_io->protocol.h264e.priv, icodec_io->protocol.h264e.priv_size);
}

static void i264e_icodec_param_init(i264e_thread_t *t, i264e_icodec_param_t *param)
{
    memset(param, 0, sizeof(i264e_icodec_param_t));
    param->index = t->h->common.param.index;
    param->protocol = C_H264E;
    param->soc = t->h->common.param.soc;
    param->module = "hwicodec";
    param->i_log_level = C_LOG_INFO;
    param->pf_log = c_log_default;
    param->ckmalloc = t->h->common.param.ckmalloc;
    param->ckfree = t->h->common.param.ckfree;
    param->ckvirt_to_phys = t->h->common.param.ckvirt_to_phys;
    param->ckphys_to_virt = t->h->common.param.ckphys_to_virt;
    param->param.h264e_param.b_tlb = t->h->common.param.b_tlb;
    param->param.h264e_param.b_use_phy = t->h->common.param.b_use_phy;
    param->param.h264e_param.b_user_fenc = t->h->common.param.b_user_fenc;
    param->param.h264e_param.i_threads = t->h->common.param.i_threads;
    param->param.h264e_param.gopsize = t->h->common.param.i_gop;
    param->param.h264e_param.i_req_rdnum = t->h->common.rdframe_cnt;
    param->param.h264e_param.i_req_rdsnum = t->h->common.rdsframe_cnt;
    param->param.h264e_param.b_mref = t->h->common.param.b_mref;
    param->param.h264e_param.b_buf_share = t->h->common.param.b_buf_share;
    param->param.h264e_param.b_use_dn = t->h->common.param.b_use_dn;
    param->param.h264e_param.b_use_ncu = t->h->common.param.b_use_ncu;
    param->param.h264e_param.daisy_chain_en = t->h->common.param.b_open_coop;
    param->param.h264e_param.i_devid = t->h->common.param.i_devid;

    param->param.h264e_param.i_csp = t->h->common.param.i_csp;
    param->param.h264e_param.i_mb_width = t->h->common.sps->i_mb_width;
    param->param.h264e_param.i_mb_height = t->h->common.sps->i_mb_height;
    param->param.h264e_param.scaling_list = t->h->common.pps->scaling_list;
    param->param.h264e_param.empty = t->h->common.reconfig.empty;

    /* Parvate tlb */
    param->param.h264e_param.private_tlb_flag = t->h->common.param.private_tlb_flag;
    param->param.h264e_param.private_tlb_vbase = t->h->common.param.private_tlb_vbase;
    param->param.h264e_param.private_tlb_pbase = t->h->common.param.private_tlb_pbase;

    /* watermark*/
    param->param.h264e_param.watermark_en = t->h->common.param.b_fisheye;
}

int i264e_icodec_init(i264e_thread_t *t)
{
    i264e_icodec_param_t param;

    i264e_icodec_param_init(t, &param);

    if ((t->h->common.icodec = hwicodec_init(&param)) == NULL) {
        i264e_log(t->h, C_LOG_ERROR, "hwicodec_init failed\n");
        goto err_hwicodec_init;
    }
    t->h->common.H264E_SliceInfoSize = param.param.h264e_param.H264E_SliceInfoSize;
    return 0;

    err_hwicodec_init:
    return -1;
}

/* return bit stream size */
int i264e_icodec_enc(i264e_thread_t *t)
{
    t->p.icodec_io.protocol.h264e.i_frame = (intptr_t)t->ip.i_frame;
    if (t->h->common.param.b_fisheye) {
        memset(t->ip.roi, 0, sizeof(c_roi_t) * 8);
        if ((t->p.sh.i_type == I264E_SLICE_TYPE_I)
            || ((t->ip.i_frame * t->h->common.param.i_fps_den) % t->h->common.param.i_fps_num) == 0) {
                t->ip.roi[0].roi_en = 1;
                t->ip.roi[0].roi_md = 0;
                t->ip.roi[0].roi_qp = t->p.i_global_qp < 15 ? t->p.i_global_qp : 15;
                t->ip.roi[0].roi_lmbx = 0;
                t->ip.roi[0].roi_rmbx = 3;
                t->ip.roi[0].roi_umby = 0;
                t->ip.roi[0].roi_bmby = 1;
            }
    }

    if (t->p.b_thread_dnstage == DENOISE_STAGE_NONE) {
        t->p.icodec_io.protocol.h264e.fenc.yaddr = (intptr_t)t->p.fenc->plane[0];
        t->p.icodec_io.protocol.h264e.fenc.caddr = (intptr_t)t->p.fenc->plane[1];
        t->p.icodec_io.protocol.h264e.fpad.yaddr = (intptr_t)t->p.fpad->plane[0];
        t->p.icodec_io.protocol.h264e.fpad.caddr = (intptr_t)t->p.fpad->plane[1];
        t->p.icodec_io.protocol.h264e.is_use_ncu = t->p.pic_in.img.is_use_ncu;
        t->p.icodec_io.protocol.h264e.ncu_middate_size = t->p.pic_in.img.ncu_middate_size;
        t->p.icodec_io.protocol.h264e.ncu_middate_p = t->p.pic_in.img.ncu_middate_p;
        t->p.icodec_io.protocol.h264e.ncu_out_p = t->p.pic_in.img.ncu_out_p;
        if (t->h->common.param.b_tlb && t->h->common.param.b_user_fenc) {
            t->p.icodec_io.protocol.h264e.fenc_size.ysize = t->p.fenc->i_stride[0] * t->p.fenc->i_lines[0];
            t->p.icodec_io.protocol.h264e.fenc_size.csize = t->p.fenc->i_stride[1] * t->p.fenc->i_lines[1];
        }
        t->p.icodec_io.protocol.h264e.i_fenc_stride[0] = C_ALIGN(t->h->common.param.i_width, 16);
        t->p.icodec_io.protocol.h264e.i_fenc_stride[1] = C_ALIGN(t->h->common.param.i_width, 16);
        t->p.icodec_io.protocol.h264e.fdec.yaddr = (intptr_t)t->p.fdec->plane[0];
        t->p.icodec_io.protocol.h264e.fdec.caddr = (intptr_t)t->p.fdec->plane[1];
        t->p.icodec_io.protocol.h264e.b_deblocking_expand = 1;
        t->p.icodec_io.protocol.h264e.i_csp = t->h->common.param.i_csp;
        //i264e_log(t->h, C_LOG_DEBUG, "no stage, qp=%d\n", t->p.i_global_qp);
    } else if (t->p.b_thread_dnstage == DENOISE_STAGE_ENC_EXT) {
        t->p.icodec_io.protocol.h264e.fenc.yaddr = (intptr_t)t->p.fenc->plane[0];
        t->p.icodec_io.protocol.h264e.fenc.caddr = (intptr_t)t->p.fenc->plane[1];
        t->p.icodec_io.protocol.h264e.fpad.yaddr = (intptr_t)t->p.fpad->plane[0];
        t->p.icodec_io.protocol.h264e.fpad.caddr = (intptr_t)t->p.fpad->plane[1];
        t->p.icodec_io.protocol.h264e.is_use_ncu = t->p.pic_in.img.is_use_ncu;
        t->p.icodec_io.protocol.h264e.ncu_middate_size = t->p.pic_in.img.ncu_middate_size;
        t->p.icodec_io.protocol.h264e.ncu_middate_p = t->p.pic_in.img.ncu_middate_p;
        t->p.icodec_io.protocol.h264e.ncu_out_p = t->p.pic_in.img.ncu_out_p;
        if (t->h->common.param.b_tlb && t->h->common.param.b_user_fenc) {
            t->p.icodec_io.protocol.h264e.fenc_size.ysize = t->p.fenc->i_stride[0] * t->p.fenc->i_lines[0];
            t->p.icodec_io.protocol.h264e.fenc_size.csize = t->p.fenc->i_stride[1] * t->p.fenc->i_lines[1];
        }
        t->p.icodec_io.protocol.h264e.i_fenc_stride[0] = C_ALIGN(t->h->common.param.i_width, 16);
        t->p.icodec_io.protocol.h264e.i_fenc_stride[1] = C_ALIGN(t->h->common.param.i_width, 16);
        t->p.icodec_io.protocol.h264e.fdec.yaddr = (intptr_t)t->h->common.extframe[t->ip.i_thread_phase].plane[0];
        t->p.icodec_io.protocol.h264e.fdec.caddr = (intptr_t)t->h->common.extframe[t->ip.i_thread_phase].plane[1];
        t->p.icodec_io.protocol.h264e.b_deblocking_expand = 0;
        t->p.icodec_io.protocol.h264e.i_csp = t->h->common.param.i_csp;
        //i264e_log(t->h, C_LOG_DEBUG, "enc_ext stage, qp=%d\n", t->p.i_global_qp);
    } else if (t->p.b_thread_dnstage == DENOISE_STAGE_EXT_ENC) {
        t->p.icodec_io.protocol.h264e.fenc.yaddr = (intptr_t)t->h->common.extframe[t->ip.i_thread_phase].plane[0];
        t->p.icodec_io.protocol.h264e.fenc.caddr = (intptr_t)t->h->common.extframe[t->ip.i_thread_phase].plane[1];
        t->p.icodec_io.protocol.h264e.fpad.yaddr = (intptr_t)t->p.fpad->plane[0];
        t->p.icodec_io.protocol.h264e.fpad.caddr = (intptr_t)t->p.fpad->plane[1];
        t->p.icodec_io.protocol.h264e.is_use_ncu = t->p.pic_in.img.is_use_ncu;
        t->p.icodec_io.protocol.h264e.ncu_middate_size = t->p.pic_in.img.ncu_middate_size;
        t->p.icodec_io.protocol.h264e.ncu_middate_p = t->p.pic_in.img.ncu_middate_p;
        t->p.icodec_io.protocol.h264e.ncu_out_p = t->p.pic_in.img.ncu_out_p;
        t->p.icodec_io.protocol.h264e.fenc_size.ysize = t->h->common.sps->i_mb_width * 16 * t->h->common.sps->i_mb_height * 16;
        t->p.icodec_io.protocol.h264e.fenc_size.csize = t->h->common.sps->i_mb_width * 16 * t->h->common.sps->i_mb_height * 8;
        t->p.icodec_io.protocol.h264e.i_fenc_stride[0] = t->h->common.sps->i_mb_width * 16;
        t->p.icodec_io.protocol.h264e.i_fenc_stride[1] = t->h->common.sps->i_mb_width * 16;
        t->p.icodec_io.protocol.h264e.fdec.yaddr = (intptr_t)t->p.fdec->plane[0];
        t->p.icodec_io.protocol.h264e.fdec.caddr = (intptr_t)t->p.fdec->plane[1];
        t->p.icodec_io.protocol.h264e.b_deblocking_expand = 1;
        t->p.icodec_io.protocol.h264e.i_csp = C_CSP_T420;
        //i264e_log(t->h, C_LOG_DEBUG, "ext_reb stage, qp=%d\n", t->p.i_global_qp);
    }

	t->p.icodec_io.protocol.h264e.buf_share_en = 0;
	t->p.icodec_io.protocol.h264e.mref_en = 0;

    if (t->p.sh.i_type == I264E_SLICE_TYPE_I) {
        t->p.icodec_io.protocol.h264e.fref.yaddr = 0;
        t->p.icodec_io.protocol.h264e.fref.caddr = 0;
		if (t->h->common.param.b_buf_share && !t->h->common.param.b_use_dn) {
			if (!t->h->common.param.b_mref) {
				t->p.icodec_io.protocol.h264e.frame_idx = 0;
				t->p.icodec_io.protocol.h264e.buf_share_en = 1;
			}
		}
    } else {
        t->p.icodec_io.protocol.h264e.fref.yaddr = (intptr_t)t->p.fref->plane[0];
        t->p.icodec_io.protocol.h264e.fref.caddr = (intptr_t)t->p.fref->plane[1];
		if (t->h->common.param.b_mref && (t->ip.i_frame > 1)) {
			t->p.icodec_io.protocol.h264e.mref_en = 1;
			t->p.icodec_io.protocol.h264e.fmref.yaddr = (intptr_t)t->p.fmref->plane[0];
			t->p.icodec_io.protocol.h264e.fmref.caddr = (intptr_t)t->p.fmref->plane[1];
		}
		/* when vpu has 2pass func, b_buf_share should be close */
		if (t->h->common.param.b_buf_share && !t->h->common.param.b_use_dn) {
			if ((!t->h->common.param.b_mref) || (t->h->common.param.b_mref && (t->ip.i_frame > 1))) {
				t->p.icodec_io.protocol.h264e.frame_idx = t->h->common.param.b_mref ? t->ip.i_frame - 2 : t->ip.i_frame;
				t->p.icodec_io.protocol.h264e.buf_share_en = 1;
			}
            //			if ((t->h->common.param.b_mref) && (t->ip.i_frame == 1)) {
			//}
		}
    }
    t->p.icodec_io.protocol.h264e.fbs = t->p.fbs;

    /* for the hwicodec only support i slice and p slice, i_type means is p slice ? */
    t->p.icodec_io.protocol.h264e.i_type = (t->p.sh.i_type != I264E_SLICE_TYPE_I);
    t->p.icodec_io.protocol.h264e.b_fast_mvp = t->h->common.param.analyse.b_fast_mvp;
    t->p.icodec_io.protocol.h264e.proi = (hwicodec_roi_t *)t->ip.roi;
    t->p.icodec_io.protocol.h264e.i_qp = t->p.i_global_qp;

    t->p.icodec_io.protocol.h264e.i_qp_min = t->h->common.param.rc.i_qp_min;
    t->p.icodec_io.protocol.h264e.i_qp_max = t->h->common.param.rc.i_qp_max;
    t->p.icodec_io.protocol.h264e.b_deblocking_filter = !t->p.sh.i_disable_deblocking_filter_idc;
    t->p.icodec_io.protocol.h264e.i_alpha_c0_offset = t->p.sh.i_alpha_c0_offset;
    t->p.icodec_io.protocol.h264e.i_beta_offset = t->p.sh.i_beta_offset;
    t->p.icodec_io.protocol.h264e.state = t->p.cabac.state;
    t->p.icodec_io.protocol.h264e.cmpx = 0;
    t->p.icodec_io.bslen = 0;

    t->p.icodec_io.protocol.h264e.i_mb_width = t->h->common.sps->i_mb_width;
    t->p.icodec_io.protocol.h264e.i_mb_height = t->h->common.sps->i_mb_height;
    t->p.icodec_io.protocol.h264e.start_mb_x = t->h->common.reconfig.start_mb_x;
    t->p.icodec_io.protocol.h264e.start_mb_y = t->h->common.reconfig.start_mb_y;
    t->p.icodec_io.protocol.h264e.prev_i_frame = t->p.prev_i_frame;
    t->p.icodec_io.protocol.h264e.gopsize = t->h->common.param.i_gop;
    t->p.icodec_io.protocol.h264e.i_chroma_qp_offset = t->h->common.param.analyse.i_chroma_qp_offset;

    if ((t->h->common.param.soc == C_T21) || (t->h->common.param.soc == C_T23)) {
        H264E_SLICE_INFO_S_T21 *si = (H264E_SLICE_INFO_S_T21*)t->p.icodec_io.protocol.h264e.H264E_SliceInfo;
		if (si->qp != t->p.i_global_qp) {
			i264e_log(t->h, C_LOG_ERROR, "si->frame_type=%d, si->qp=%d, si->base_qp=%d, t->p.i_global_qp=%d\n", si->frame_type, si->qp, si->base_qp, t->p.i_global_qp);
			assert(si->qp == t->p.i_global_qp);
		}
        si->frame_type      = (t->p.sh.i_type != I264E_SLICE_TYPE_I);
        si->qp              = t->p.i_global_qp;
        si->base_qp         = t->p.i_global_qp;
        si->state           = t->p.cabac.state;
        si->alpha_c0_offset = t->p.sh.i_alpha_c0_offset;
        si->beta_offset     = t->p.sh.i_beta_offset;
        si->cqp_offset      = t->h->common.param.analyse.i_chroma_qp_offset;
    }
    return hwicodec_process(t->h->common.icodec, &t->p.icodec_io);
}

void i264e_icodec_deinit(i264e_thread_t *t)
{
    if (t->h->common.icodec != NULL) {
        hwicodec_deinit(t->h->common.icodec);
    }
}

static int i264e_vbinfo_check(i264e_thread_t *t, const i264e_vbinfo_t const *vbinfo, const int num)
{
    int i = 0, j = 0;
    int i_threads = t->h->common.param.i_threads;
    int refnum = t->h->common.rdframe_cnt;
	int refsnum = t->h->common.rdsframe_cnt;
    int fencnum = t->h->common.param.b_user_fenc == 1 ? 0 : i_threads;
    int bsnum = i_threads;
    int extnum = t->h->common.param.b_use_dn != 0 ? i_threads : 0;

    if (((num != C_SOC_VB_MAX) || (vbinfo == NULL))
        || ((vbinfo[C_SOC_VB_RD].type != C_SOC_VB_RD) || (vbinfo[C_SOC_VB_RD].num != refnum))
		|| ((vbinfo[C_SOC_VB_RDS].type != C_SOC_VB_RDS) || (vbinfo[C_SOC_VB_RDS].num != refsnum))
        || ((vbinfo[C_SOC_VB_ENC].type != C_SOC_VB_ENC) || (vbinfo[C_SOC_VB_ENC].num != fencnum))
        || ((vbinfo[C_SOC_VB_BS].type != C_SOC_VB_BS) || (vbinfo[C_SOC_VB_BS].num != bsnum))
        || ((vbinfo[C_SOC_VB_EXT].type != C_SOC_VB_EXT) || (vbinfo[C_SOC_VB_EXT].num != extnum))) {
            i264e_log(t->h, C_LOG_ERROR, "vbinfo checkout failed, num = %d\n", num);
            goto err_vbinfo;
        }

    for (i = 0; i < C_SOC_VB_MAX; i++) {
        int padh = 0, padv = 0;
        if ((i != C_SOC_VB_RD) && (i != C_SOC_VB_ENC) && (i != C_SOC_VB_EXT) && (i != C_SOC_VB_RDS)) {
            continue;
        }
        if ((i == C_SOC_VB_RD) || (i == C_SOC_VB_RDS) || (i == C_SOC_VB_EXT)) {
            padh = C_PADH * 2;
            padv = C_PADV * 2;
        }
        for (j = 0; j < vbinfo[i].num; j++) {
            if ((vbinfo[i].vb[j].yptr == 0) || (vbinfo[i].vb[j].cptr == 0)
                || (vbinfo[i].vb[j].i_stride < (C_ALIGN(t->h->common.param.i_width, 16) + padh))
                || (vbinfo[i].vb[j].i_lines < (C_ALIGN(t->h->common.param.i_height, 16) + padv))) {
                    i264e_log(t->h, C_LOG_ERROR, "vbinfo.vb checkout failed\n");
                    goto err_vbinfo_vb;
                }
        }
    }

    return 0;

    err_vbinfo_vb:
    err_vbinfo:
    return -1;

}

static inline void i264e_icodec_init_frame(i264e_vb_t *vb, i264e_frame_t *frame)
{
    int i = 0;
    for (i = 0; i < 2; i++) {
        frame->i_stride[i] = vb->i_stride;
        frame->i_lines[i] = vb->i_lines / ((i == 0) ? 1 : 2);
        frame->plane[i] = (pixel *)((i == 0) ? vb->yptr : vb->cptr);
    }
}

int i264e_icodec_request_vbinfo(i264e_thread_t *t)
{
    int i = 0, j = 0;
    i264e_common_t *c = &t->h->common;
    int	vbnum = 0;
    i264e_frame_t *frame = NULL;

    if (hwicodec_request_vbinfo(c->icodec, &c->vbinfo, &vbnum) < 0) {
        i264e_log(t->h, C_LOG_ERROR, "icodec_request_vbinfo failed\n");
        goto err_icodec_request_vbinfo;
    }

    if (i264e_vbinfo_check(t, c->vbinfo, vbnum) < 0) {
        i264e_log(t->h, C_LOG_ERROR, "vbinfo_check failed\n");
        goto err_vbinfo_check;
    }

	i264e_check(c->rdframe_cnt == c->vbinfo[C_SOC_VB_RD].num, "Space allocation error:rdframe_cnt=%d,vbinfo[C_SOC_VB_RD].num=%d\n",c->rdframe_cnt,c->vbinfo[C_SOC_VB_RD].num);
	i264e_check(c->rdsframe_cnt == c->vbinfo[C_SOC_VB_RDS].num, "Space allocation error:rdfsrame_cnt=%d,vbinfo[C_SOC_VB_RDS].num=%d\n",c->rdsframe_cnt,c->vbinfo[C_SOC_VB_RDS].num);
	i264e_check(c->encframe_cnt == c->vbinfo[C_SOC_VB_ENC].num, "Space allocation error:encframe_cnt=%d,vbinfo[C_SOC_VB_ENC].num=%d\n",c->encframe_cnt,c->vbinfo[C_SOC_VB_ENC].num);
    c->rdframe_cnt = c->vbinfo[C_SOC_VB_RD].num;
	c->rdsframe_cnt = c->vbinfo[C_SOC_VB_RDS].num;
    c->encframe_cnt = c->vbinfo[C_SOC_VB_ENC].num;
    for (i = 0; i < C_SOC_VB_MAX; i++) {
        if ((i != C_SOC_VB_RD) && (i != C_SOC_VB_RDS) && (i != C_SOC_VB_ENC) && (i != C_SOC_VB_EXT	)) {
            continue;
        }
        frame = (i == C_SOC_VB_RD) ? c->rdframe : (i == C_SOC_VB_RDS) ? c->rdsframe : ((i == C_SOC_VB_ENC) ? c->encframe : c->extframe);
        for (j = 0; j < c->vbinfo[i].num; j++) {
            i264e_icodec_init_frame(&c->vbinfo[i].vb[j], &frame[j]);
        }
    }

    for (i = 0; i < t->h->common.param.i_threads; i++) {
        t->h->threads[i]->p.fbs = (uint8_t *)c->vbinfo[C_SOC_VB_BS].vb[i].yptr;
        t->h->threads[i]->p.vpupri = (unsigned int *)c->vbinfo[C_SOC_VB_DMA].vb[i].yptr;
		i264e_log(t->h, C_LOG_DEBUG, "t->h->threads[%d]->p.fbs=%p, t->h->threads[%d]->p.vpupri=%p\n", i, t->h->threads[i]->p.fbs, i, t->h->threads[i]->p.vpupri);
    }

    return 0;

    err_vbinfo_check:
    err_icodec_request_vbinfo:
    return -1;
}

void i264e_icodec_release_vbinfo(i264e_thread_t *t)
{
    hwicodec_release_vbinfo(t->h->common.icodec, t->h->common.vbinfo);
}

static int cabac_range[] = {
    504,493,482,471,460,449,439,429,419,
    409,399,389,379,370,361,352,343,334,
    325,316,308,300,292,284,276,268,260,
};

/* return black bit stream size */
int i264e_icodec_black(i264e_thread_t *t)
{
    i264e_cabac_t cb;
    int mbw = t->h->common.sps->i_mb_width;
    int mbh = t->h->common.sps->i_mb_height;
    int zerolen = (mbw * mbh - 135) / 216;

    t->p.fbs[0] = 0xfe;
    t->p.fbs[1] = 0xda;
    t->p.fbs[2] = 0xa6;
    t->p.fbs[3] = 0x58;
    memset(t->p.fbs + 4, 0, zerolen + 1);

    memset(&cb, 0, sizeof(i264e_cabac_t));
    cb.i_low = 0;
    cb.i_range = cabac_range[(mbw * mbh - 135) % 27];
    cb.i_queue = ((mbw * mbh - 135) % 216) / 27 - 8;
    cb.i_bytes_outstanding = 0;
    cb.p = t->p.fbs + zerolen + 4 + 1;
    i264e_cabac_encode_flush(t, &cb);
    t->p.icodec_io.bslen = cb.p - t->p.fbs;

    return 0;
}
